--[[
    函数：lua2_lab_search
    功能：搜索模版
    参数：ARGV[1]:types, 分类ID字符串,以半角逗号分割，形如：'1,2,3'
    参数：ARGV[2]:industries，行业标签ID字符串,以半角逗号分割，形如：'1,2,3'
    参数：ARGV[3]:scenes,场景标签ID字符串,以半角逗号分割，形如：'1,2,3'
    参数：ARGV[4]:colors，颜色标签ID字符串,以半角逗号分割，形如：'1,2,3'
    参数：ARGV[5]:styles，风格标签ID字符串,以半角逗号分割，形如：'1,2,3'
    参数：ARGV[6]:customs,自定义标签ID字符串,以半角逗号分割，形如：'1,2,3'
    参数：ARGV[7]:limit，返回的最小个数限制
    参数：ARGV[8]:page_num，页码，从0开始
    参数：ARGV[9]:page_size，页容量
    返回：返回数组：
            0:code,0表示成功，其它表示没搜索到结果中途退出
            1:result，搜索结果
            2:count，总页数
--]]

-- ########################################################

-- 分类的前缀
local PREFIX_TYPE = "lab_type_"
-- 标签的前缀
local PREFIX_LABEL_INDUSTRY = "lab_label_industry_"
local PREFIX_LABEL_SCENE = "lab_label_scene_"
local PREFIX_LABEL_STYLE = "lab_label_style_"
local PREFIX_LABEL_COLOR = "lab_label_color_"
local PREFIX_LABEL_CUSTOM = "lab_label_custom_"

-- 排序的key
local KEY_SORT = "lab_sort"
-- ID-模版缓存的前缀
local PREFIX_DESIGN = "lab_d_"

-- key的连接符号
local PREFIX_S = "*"
-- 数组字符串的分割符号
local PREFIX_ARRAY_SPLIT = ","

-- ########################################################
-- [局部函数] 组成返回值数组，第0个是错误码，第1个是搜索结果，第2个是总页数
local retData = function(code,result,count)
    local ret = {}
    table.insert(ret, code)

    if(result==nil)
    then
        result='[]'
    end
    table.insert(ret, result)

    if(count==nil)
    then
        count=0
    end
    table.insert(ret, count)
    return ret
end


-- ########################################################
-- [局部函数] 动态参数调用redis.call，用于集合的计算
local dynamicParaCall = function(cmd,key,array)
    -- https://stackoverflow.com/questions/42291690/redis-lua-dynamic-number-of-keys-in-redis-call
    local args = {cmd, key}
    local len = #array
    for i= 1, len do
        local key = array[i]
        table.insert(args, key)
    end

    -- unpack the table and pass to redis.pcall
    redis.call(unpack(args))
end

-- ########################################################
-- [局部函数] 动态参数调用redis.call,并向数组中增加0，用于有序集合和集合的计算
local dynamicParaCallZAdd = function(key,array)
    -- https://stackoverflow.com/questions/42291690/redis-lua-dynamic-number-of-keys-in-redis-call
    local args = {'zadd', key}
    local len = #array
    for i= 1, len do
        local mem = array[i]
        table.insert(args, 0)
        table.insert(args, mem)
    end

    -- unpack the table and pass to redis.pcall
    redis.call(unpack(args))
end

-- ########################################################
-- [局部函数] 搜索某个分类下的数据
-- 参数：argv      :参数, ARGV[n]
-- 参数：prefix    :标签的前缀，形如：PREFIX_LABEL_*
-- 参数：A         :A 上一步处理得到的集合
-- 参数：B         :B 本处理用到的临时集合
-- 参数：TEMP      :TEMP 本处理用到的临时集合
-- 参数：s         :通配符 PREFIX_S
-- 参数：split     :分割符,PREFIX_ARRAY_SPLIT
-- 参数：min       :最小返回个数，MIN_RET_COUNT
-- 参数：func      :动态命令函数 dynamicParaCall
-- 返回：0表示成功，其它表示失败原因
local searchSetOfType = function(argv, prefix,   A,B, C,TEMP, s,split, min, func)
    -- 删除旧数据
    redis.call('del',A)
    redis.call('del',B)
    redis.call('del',C)
    redis.call('del',TEMP)

    -- 分类ID数组types为空？
    local no_type_flg=0
    local types = string.split(argv, split)
    local t_len = 0
    if(types==nil)
    then
        no_type_flg=1
    else
        t_len = #types
        if(t_len==0)
        then
            no_type_flg=1
        end
    end

    if(no_type_flg==1)
    then
        -- 如果types为空，则求D1的并集A
        local key = prefix..s
        local l_keys = redis.call('keys',key)
        local l_len = #l_keys
        if(l_len<=1)
        then
            -- 内部数据错误：types数据错误
            return 1
        else
            func('SUNIONSTORE', A, l_keys)
        end

    else
        -- 如果types有值，则根据types筛选D1后，求并集A
        local array = {}
        for i= 1, t_len do
            local key1 = prefix..types[i]
            table.insert(array, key1)
        end
        func('SUNIONSTORE', A, array)
    end


    -- 如果A个数不够，则返回失败
    local A_len = redis.call('SCARD',A)
    if(A_len<min)
    then
        return 1
    else
        return 0
    end
end



-- ########################################################
-- 搜索某个标签下的数据
-- 参数：argv      :参数, ARGV[n]
-- 参数：prefix    :标签的前缀，形如：PREFIX_LABEL_*
-- 参数：A         :A 上一步处理得到的集合
-- 参数：B         :B 本处理用到的临时集合
-- 参数：C         :C 本处理用到的临时集合
-- 参数：TEMP      :TEMP 本处理用到的临时集合
-- 参数：s         :通配符 PREFIX_S
-- 参数：split     :分割符,PREFIX_ARRAY_SPLIT
-- 参数：min       :最小返回个数，MIN_RET_COUNT
-- 参数：func      :动态命令函数 dynamicParaCall
-- 返回：0表示成功，其它表示失败原因
local searchSetOfLabel = function(argv, prefix,  A,B,C, TEMP, s, split, min, func)
    -- 删除旧数据
    redis.call('del',B)
    redis.call('del',C)
    redis.call('del',TEMP)

    -- 行业标签ID字符串为空？
    local no_label_flg=0
    local industries = string.split(argv, split)
    local label_len = 0
    if(industries==nil)
    then
        no_label_flg=1
    else
        label_len = #industries
        if(label_len==0)
        then
            no_label_flg=1
        end
    end

    if(no_label_flg==0)
    then
        -- 如果labels有值，则先lables内部求并集，再与A求交集
        local array = {}
        for i= 1, label_len do
            local key1 = prefix..industries[i]
            table.insert(array, key1)
        end
        func('SUNIONSTORE', B, array)

        -- 再与A求交集
        local array = {A,B}
        func('SINTERSTORE', B, array)

        if(prefix == PREFIX_LABEL_INDUSTRY) then
            -- '行业': 只求交集
            redis.call('SUNIONSTORE',A,B,TEMP)
        else
            -- 检测交集结果是否足够，如果不够，则求该preifx下面的并集C,让后求AC交集
            local B_len = redis.call('SCARD',B)
            if(B_len<min)
            then
                -- 求该preifx下面的并集C
                local key = prefix..s
                local keys = redis.call('keys',key)
                local len = #keys
                local array = {}
                for i= 1, len do
                    local key1 = keys[i]
                    table.insert(array, key1)
                end
                func('SUNIONSTORE', C, array)

                -- 让后求AC交集
                redis.call('SINTERSTORE',A,A,C)
            else
                -- 如果交集数据足够，则B赋值给A
                redis.call('SUNIONSTORE',A,B,TEMP)
            end
        end

    else
        -- 没选'行业'标签,返回失败
        if(prefix == PREFIX_LABEL_INDUSTRY) then
            return 101;
        end
    end

    -- 如果A个数不够，则返回失败
    local A_len = redis.call('SCARD',A)
    if(A_len<min)
    then
        return 1
    else
        --  把B赋值给A
        return 0
    end
end


-- ########################################################
-- A,B作为中间结果
local A='A'
local B='B'
local C='C'
local TEMP='temp'

-- ########################################################
local limit = tonumber(ARGV[7]) or 0
-- ########################################################
-- 处理分类
local s1 = searchSetOfType(ARGV[1], PREFIX_TYPE,A,B, C, TEMP, PREFIX_S,PREFIX_ARRAY_SPLIT, limit,  dynamicParaCall)
if(s1~=0)
then
    -- 内部数据错误：types数据错误
    return retData(1,nil,nil)
end

-- ########################################################
-- 处理标签
local s1 = searchSetOfLabel(ARGV[2], PREFIX_LABEL_INDUSTRY,A,B,C, TEMP,PREFIX_S,  PREFIX_ARRAY_SPLIT, limit,  dynamicParaCall)
if(s1~=0)
then
    -- 内部数据错误：types数据错误
    return retData(10,nil,nil)
end
--[[ 不搜索场景
local s1 = searchSetOfLabel(ARGV[3], PREFIX_LABEL_SCENE,A,B,C, TEMP,PREFIX_S, PREFIX_ARRAY_SPLIT, limit,  dynamicParaCall)
if(s1~=0)
then
    -- 内部数据错误：types数据错误
    return retData(11,nil,nil)
end
--]]
local s1 = searchSetOfLabel(ARGV[4], PREFIX_LABEL_COLOR,A,B,C, TEMP,PREFIX_S,PREFIX_ARRAY_SPLIT, limit,  dynamicParaCall)
if(s1~=0)
then
    -- 内部数据错误：types数据错误
    return retData(12,nil,nil)
end

local s1 = searchSetOfLabel(ARGV[5], PREFIX_LABEL_STYLE,A,B,C, TEMP,PREFIX_S, PREFIX_ARRAY_SPLIT, limit,  dynamicParaCall)
if(s1~=0)
then
    -- 内部数据错误：types数据错误
    return retData(13,nil,nil)
end

--[[  不搜索自定义
local s1 = searchSetOfLabel(ARGV[7], PREFIX_LABEL_CUSTOM,A,B,C, TEMP,PREFIX_S, PREFIX_ARRAY_SPLIT, limit,  dynamicParaCall)
if(s1~=0)
then
    -- 内部数据错误：types数据错误
    return retData(14,nil,nil)
end
--]]


-- ########################################################
-- 计算总页数
local page_num = tonumber(ARGV[8]) or 0
local page_size = tonumber(ARGV[9]) or 0
local A_len = redis.call('SCARD',A)
local count = math.floor((A_len+page_size-1)/page_size)

-- ########################################################
-- 按sort进行排序
local start_num = page_num*page_size
local end_num = start_num+page_size-1

local ids = redis.call('SMEMBERS',A)
-- 使用B、C前先清空
redis.call('del',B)
redis.call('del',C)
dynamicParaCallZAdd(B, ids)
redis.call('ZINTERSTORE',C,2,B,KEY_SORT)
-- 按递减排序
local pageIds =redis.call('ZREVRANGE',C,start_num,end_num)


-- ########################################################
-- 返回搜索结果
-- 提取当前页数据
local pageIdsLen = #pageIds
local result='['
for i= 1, pageIdsLen do
    local one_key = PREFIX_DESIGN..pageIds[i]
    local one_result = redis.call('hget',one_key, 'search_result')
    if(i>1)
    then
        -- 拼接符号
        result = result..PREFIX_ARRAY_SPLIT
    end
    result = result..one_result
end
local result=result..']'



-- ########################################################
--[[
-- 测试代码：返回A的数据
local TEST = redis.call('SMEMBERS',A)
if(1)
then
    return TEST
end
--]]




-- ########################################################
return retData(0,result,count)